package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.search.SearchClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.api.dto.user.UserDetailDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.QuestionFormDTO;
import com.tianji.learning.domain.query.QuestionAdminPageQuery;
import com.tianji.learning.domain.query.QuestionPageQuery;
import com.tianji.learning.domain.vo.QuestionAdminVO;
import com.tianji.learning.domain.vo.QuestionVO;
import com.tianji.learning.domain.po.InteractionQuestion;
import com.tianji.learning.domain.po.InteractionReply;
import com.tianji.learning.enums.QuestionStatus;
import com.tianji.learning.mapper.InteractionQuestionMapper;
import com.tianji.learning.mapper.InteractionReplyMapper;
import com.tianji.learning.service.IInteractionQuestionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 互动提问的问题表 服务实现类
 * </p>
 *
 * @author XinxuanZhuo
 * @since 2024-02-27
 */
@SuppressWarnings("ALL")
@Service
@RequiredArgsConstructor
public class InteractionQuestionServiceImpl extends ServiceImpl<InteractionQuestionMapper, InteractionQuestion> implements IInteractionQuestionService {

    // 远程用户微服务
    private final UserClient userClient;

    // 评论表mapper
    private final InteractionReplyMapper replyMapper;

    // 分类缓存服务
    private final CategoryCache categoryCache;

    // 搜索服务（根据课程名称搜索用）
    private final SearchClient searchClient;

    // 课程服务
    private final CourseClient courseClient;

    // 章节服务
    private final CatalogueClient catalogueClient;


    /**
     * 新增提问
     */
    @Override
    public void saveQuestion(QuestionFormDTO questionFormDTO) {
        // 获取当前用户
        Long userId = UserContext.getUser();
        // dto转po
        InteractionQuestion interactionQuestion = BeanUtils.copyBean(questionFormDTO, InteractionQuestion.class);
        interactionQuestion.setUserId(userId);
        // 保存
        this.save(interactionQuestion);
    }


    /**
     * 修改提问
     */
    @Override
    public void editQuestion(Long id, QuestionFormDTO questionFormDTO) {
        // 参数校验
        if (questionFormDTO.getTitle() == null || questionFormDTO.getDescription() == null) {
            throw new BadRequestException("请求参数不合法");
        }
        // 获取当前用户id
        Long userId = UserContext.getUser();
        // 根据id查出旧的问题信息
        InteractionQuestion question = lambdaQuery()
                .eq(InteractionQuestion::getId, id)
                .one();
        if (ObjectUtils.isEmpty(question)) {
            throw new BadRequestException("请求参数不合法，问题id有误");
        }
        if (!userId.equals(question.getUserId())) {
            throw new BadRequestException("不能修改别人的问题");
        }
        // 更新
        if (questionFormDTO.getAnonymity() != null) {
            question.setAnonymity(questionFormDTO.getAnonymity());
        }
        question.setDescription(questionFormDTO.getDescription());
        question.setTitle(questionFormDTO.getTitle());
        updateById(question);
    }


    /**
     * 用户端分页查询问题列表
     */
    @Override
    public PageDTO<QuestionVO> queryQuestionPage(QuestionPageQuery pageQuery) {
        // 参数校验，课程id和小节id不能都为空
        Long courseId = pageQuery.getCourseId();
        Long sectionId = pageQuery.getSectionId();
        if (courseId == null && sectionId == null) {
            throw new BadRequestException("课程id和小节id不能都为空");
        }
        // 获取当前用户id
        Long userId = UserContext.getUser();
        // 查问题表-->page
        Page<InteractionQuestion> page = this.lambdaQuery()
                // 根据原型图需求 过滤掉描述字段
                .select(InteractionQuestion.class, questions -> !questions.getProperty().equals("description"))
                .eq(pageQuery.getOnlyMine(), InteractionQuestion::getUserId, userId)
                .eq(courseId != null, InteractionQuestion::getCourseId, pageQuery.getCourseId())
                .eq(sectionId != null, InteractionQuestion::getSectionId, pageQuery.getSectionId())
                // 只能查询未被管理员隐藏的问题
                .eq(InteractionQuestion::getHidden, false)
                // 默认按照create_time倒序排序
                .page(pageQuery.toMpPageDefaultSortByCreateTimeDesc());
        // 获取查询的问题列表
        List<InteractionQuestion> questions = page.getRecords();
        // 为空直接返回空
        if (CollUtils.isEmpty(questions)) {
            return PageDTO.empty(page);
        }
        // 根据问题表的latestAnswerId去评论表查userId（最新的回答者昵称）、最新的回答信息latestReplyContent
        Set<Long> userIds = new HashSet<>();
        Set<Long> answerIds = new HashSet<>();
        for (InteractionQuestion q : questions) {
            // 只查询非匿名问题的用户信息
            if (!q.getAnonymity()) {
                userIds.add(q.getUserId());
            }
            answerIds.add(q.getLatestAnswerId());
        }
        // 如果是一级，则上级回答id就是null，不需要再去评论表中进行查询，也预防空指针
        answerIds.remove(null);
        // key是评论表id value是po
        Map<Long, InteractionReply> replyMap = new HashMap<>(answerIds.size());
        // 根据id查询最近一次回答
        if (CollUtils.isNotEmpty(answerIds)) {
            List<InteractionReply> replies = replyMapper.selectBatchIds(answerIds);
            // 循环取出其中非匿名回答的用户id，也保存评论表的实体信息到map中用于封装新回答的信息
            for (InteractionReply reply : replies) {
                replyMap.put(reply.getId(), reply);
                // 匿名用户不做查询
                if (!reply.getAnonymity()) {
                    userIds.add(reply.getUserId());
                }
            }
        }
        // 调用远程user服务
        userIds.remove(null);
        Map<Long, UserDTO> userMap = new HashMap<>(userIds.size());
        if (CollUtils.isNotEmpty(userIds)) {
            List<UserDTO> users = userClient.queryUserByIds(userIds);
            userMap = users.stream()
                    .collect(Collectors.toMap(UserDTO::getId, u -> u));
        }
        // 封装vo
        List<QuestionVO> questionVOList = new ArrayList<>(questions.size());
        for (InteractionQuestion q : questions) {
            // 将po转vo
            QuestionVO vo = BeanUtils.copyBean(q, QuestionVO.class);
            // 不是匿名才有返回userId，这里先删除
            vo.setUserId(null);
            // 封装提问者信息（非匿名）
            if (!q.getAnonymity()) {
                UserDTO userDTO = userMap.get(q.getUserId());
                if (userDTO != null) {
                    vo.setUserId(userDTO.getId());
                    vo.setUserName(userDTO.getName());
                    vo.setUserIcon(userDTO.getIcon());
                }
            }
            // 封装最近一次回答的信息
            // 4.3.封装最近一次回答的信息
            InteractionReply reply = replyMap.get(q.getLatestAnswerId());
            if (reply != null) {
                vo.setLatestReplyContent(reply.getContent());
                // 匿名用户直接忽略
                if (!reply.getAnonymity()) {
                    UserDTO user = userMap.get(reply.getUserId());
                    vo.setLatestReplyUser(user.getName());
                }
            }
            // 添加到list
            questionVOList.add(vo);
        }
        // 返回
        return PageDTO.of(page, questionVOList);
    }

    /**
     * 用户端根据问题id查询问题详情
     */
    @Override
    public QuestionVO queryQuestionById(Long id) {
        QuestionVO vo;
        // 根据id查询问题表 得到questionDO
        InteractionQuestion questionDO = this.baseMapper.selectById(id);
        if (questionDO == null || questionDO.getHidden()) {
            //没有数据或者管理员隐藏
            return null;
        }
        vo = BeanUtils.copyBean(questionDO, QuestionVO.class);
        // 根据questionDO的userId去查用户信息（条件是该问题非匿名）
        Boolean isAnonymity = questionDO.getAnonymity();
        // 非匿名
        if (!isAnonymity) {
            UserDTO userDTO = userClient.queryUserById(questionDO.getUserId());
            if (userDTO != null) {
                vo.setUserName(userDTO.getName());
                vo.setUserIcon(userDTO.getIcon());
            }
        }
        // 如果匿名，需要将拷贝过来的userId设置成null
        vo.setUserId(null);
        return vo;
    }


    /**
     * - 查询问题是否存在
     * - 判断是否是当前用户提问的
     * - 如果不是则报错
     * - 如果是则删除问题
     * - 然后删除问题下的回答及评论
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteQuestionById(Long id) {
        // 获取当前用户id
        Long userId = UserContext.getUser();
        // 查询问题（无需判断隐藏，因为只有是非隐藏的问题才能被用户自己删除）
        InteractionQuestion questionDO = this.getById(id);
        if (questionDO == null) {
            throw new BadRequestException("请求参数非法");
        }
        // 判断是否是当前用户提问
        if (!userId.equals(questionDO.getUserId())) {
            throw new BadRequestException("只能删除自己的问题");
        }
        // 删除问题，删除问题下的回答和评论
        boolean removeQuestionFlag = this.removeById(id);
        int removeRepliesFlag = replyMapper.deleteRepliesByQuestionId(id);
        // 魔法值在这个项目不处理，阿里巴巴规范中不允许出现，可以用枚举来代替
        if (!removeQuestionFlag && removeRepliesFlag != 1) {
            throw new DbException("数据库操作异常");
        }
    }

    /**
     * 管理端分页查询互动问题
     */
    @Override
    public PageDTO<QuestionAdminVO> queryQuestionPageAdmin(QuestionAdminPageQuery query) {
        // 处理课程名称（按课程名称搜索）得到课程id
        List<Long> courseIds = null;
        if (!(query.getCourseName() == null)) {
            // 走的是es，课程在上架的时候会发mq消息写到es里面
            courseIds = searchClient.queryCoursesIdByName(query.getCourseName());
            if (CollUtils.isEmpty(courseIds)) {
                return PageDTO.empty(new Page<>());
            }
        }
        // 根据条件分页查询
        Integer status = query.getStatus();
        LocalDateTime begin = query.getBeginTime();
        LocalDateTime end = query.getEndTime();
        // 获取page
        Page<InteractionQuestion> page = this.lambdaQuery()
                .in(courseIds != null, InteractionQuestion::getCourseId, courseIds)
                .eq(status != null, InteractionQuestion::getStatus, status)
                .gt(begin != null, InteractionQuestion::getCreateTime, begin)
                .lt(end != null, InteractionQuestion::getCreateTime, end)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        // 获取questions
        List<InteractionQuestion> questions = page.getRecords();
        if (CollUtils.isEmpty(questions)) {
            return PageDTO.empty(page);
        }

        // VO需要： 用户信息、课程信息、分类信息（questions中没有的）
        Set<Long> userIdSet = new HashSet<>();
        Set<Long> courseIdSet = new HashSet<>();
        Set<Long> cataIdSet = new HashSet<>();
        // 获取各种数据的Id集合
        for (InteractionQuestion q : questions) {
            userIdSet.add(q.getUserId());
            courseIdSet.add(q.getCourseId());
            cataIdSet.add(q.getChapterId());
            cataIdSet.add(q.getSectionId());
        }
        // 根据用户id查找用户
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIdSet);
        Map<Long, UserDTO> userMap = new HashMap<>();
        if (!userDTOS.isEmpty()) {
            userMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, u -> u));
        }
        // 根据课程id查找课程
        List<CourseSimpleInfoDTO> cins = courseClient.getSimpleInfoList(courseIdSet);
        Map<Long, CourseSimpleInfoDTO> courseSimpleInfoDTOMap = new HashMap<>();
        if (!cins.isEmpty()) {
            courseSimpleInfoDTOMap = cins.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        }
        // 根据章节id查找章节
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(cataIdSet);
        Map<Long, String> cataNameMap = new HashMap<>();
        if (!cataSimpleInfoDTOS.isEmpty()) {
            cataNameMap = cataSimpleInfoDTOS.stream().collect(Collectors.toMap
                    (CataSimpleInfoDTO::getId, CataSimpleInfoDTO::getName));
        }

        // 4.封装VO
        List<QuestionAdminVO> voList = new ArrayList<>(questions.size());
        for (InteractionQuestion q : questions) {
            // pojo转vo
            QuestionAdminVO questionAdminVO = BeanUtils.copyBean(q, QuestionAdminVO.class);
            // 用户信息
            UserDTO userDTO = userMap.get(q.getUserId());
            if (userDTO != null) {
                questionAdminVO.setUserName(userDTO.getName());
                questionAdminVO.setUserIcon(userDTO.getIcon());
            }
            // 课程及分类信息
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseSimpleInfoDTOMap.get(q.getCourseId());
            if (courseSimpleInfoDTO != null) {
                questionAdminVO.setCourseName(courseSimpleInfoDTO.getName());
                questionAdminVO.setCategoryName(categoryCache.getCategoryNames(courseSimpleInfoDTO.getCategoryIds()));
            }
            // 章节信息
            questionAdminVO.setChapterName(cataNameMap.getOrDefault(q.getChapterId(), ""));
            questionAdminVO.setSectionName(cataNameMap.getOrDefault(q.getSectionId(), ""));

            voList.add(questionAdminVO);
        }

        return PageDTO.of(page, voList);
    }


    /**
     * 管理端隐藏或者显示问题
     */
    @Override
    public void changeQuestionHidden(Long id, Boolean hidden) {
        boolean update = this.lambdaUpdate()
                .eq(InteractionQuestion::getId, id)
                .set(InteractionQuestion::getHidden, hidden)
                .update();
        if (!update) {
            throw new DbException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "更新问题可见性失败");
        }
    }


    /**
     * 管理端根据id查询问题详情
     */
    @Override
    public QuestionAdminVO queryQuestionByIdInAdmin(Long id) {
        // 根据id查询问题列表 得到questionDO
        InteractionQuestion questionDO = this.getById(id);
        // 没有的话返回null
        if (questionDO == null) {
            return null;
        }
        // copyBean do-->vo
        QuestionAdminVO vo = BeanUtils.copyBean(questionDO, QuestionAdminVO.class);

        // 根据do的userId去查用户信息
        Long userId = questionDO.getUserId();
        UserDTO userInfo = userClient.queryUserById(userId);
        if (userInfo != null) {
            vo.setUserName(userInfo.getName());
            vo.setUserIcon(userInfo.getIcon());
        }

        // 根据courseid去查分类、课程、老师信息
        Long courseId = questionDO.getCourseId();
        CourseFullInfoDTO courseInfo = courseClient.
                getCourseInfoById(courseId, true, true);
        if (courseInfo != null) {
            // 课程名
            vo.setCourseName(courseInfo.getName());

            // 老师信息
            // 老师信息package com.tianji.course.service.impl;  得到老师id，去用户表查
            Long cOneTeacherId = courseInfo.getTeacherIds().get(0);
            UserDetailDTO userDetailInfo = userClient.queryUserDetailsById(cOneTeacherId);
            if (userDetailInfo != null) {
                vo.setTeacherName(userDetailInfo.getName());
            }

            //分类信息
            List<Long> categoryIds = courseInfo.getCategoryIds();
            String categoryNames = categoryCache.getCategoryNames(categoryIds);
            vo.setCategoryName(categoryNames);


        }
        // 根据章节id去查章节信息
        Long chapterId = questionDO.getChapterId();
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(List.of(chapterId));
        if (CollUtils.isNotEmpty(cataSimpleInfoDTOS)) {
            vo.setChapterName(cataSimpleInfoDTOS.get(0).getName());
        }

        // 管理员查看了问题，根据do把status更新成已查看
        questionDO.setStatus(QuestionStatus.CHECKED);
        this.updateById(questionDO);
        return vo;
    }
}
